#ifndef SHADER_HPP
#define SHADER_HPP

//-----------------------------------------------------------------------------
// Includes
//-----------------------------------------------------------------------------
#include <vector>
#include <string>
#include <map>
#include <Settings.hpp>
#include <Math/Matrix.hpp>
#include <Tools/SmartPointer.hpp>
#include <Rendering/Enumeration.hpp>
#include <Geometry/Mesh.hpp>


namespace Rendering {

	//-----------------------------------------------------------------------------
	// Locations
	//-----------------------------------------------------------------------------
	typedef std::vector< std::string > TStringVector;
	//-----------------------------------------------------------------------------	
	class ShaderLocalizator {
		public:
			ShaderLocalizator() { }
			void AddLocation(const std::string& name, int location);			
			void AddLocation(const std::string& name, int location, int unit);
			bool FindLocation(const std::string& name, int& location) const;
			bool FindLocation(const std::string& name, int& location, int& unit) const;
			bool FindLocation(VertexBufferType type, int& location) const;
			void GetAllLocation(TStringVector& names) const;
		private:	
			// Forbid methods		
			ShaderLocalizator(const ShaderLocalizator& copy);
			ShaderLocalizator operator=(const ShaderLocalizator& copy);
			// Type definition & attributes			
			struct LocationDetail { int location, unit; };
			typedef std::map<std::string, LocationDetail > TLocationMap;
			TLocationMap ressourceLocations;
	};	
	//-----------------------------------------------------------------------------
	class ShaderCompilationReport {
		public:
			ShaderCompilationReport():validCompilation(true) { }
			~ShaderCompilationReport() { }
			void Report(const std::string& name, int location, bool status);
			void Report(const std::string& name, int location, int unit, bool status);
			void Check(const ShaderLocalizator& localizator);
			bool IsCompile() const;
			void Print() const;
		private:			
			// Forbid methods		
			ShaderCompilationReport(const ShaderCompilationReport& copy);
			ShaderCompilationReport operator=(const ShaderCompilationReport& copy);
			// Type definition & attributes									
			TStringVector report;
			TStringVector locationNames;
			bool validCompilation;
	};
	//-----------------------------------------------------------------------------
	class IShaderLocation {
		public:
			IShaderLocation(const std::string& n): name(n), location(DEFAULT_LOCATION) { }	
			virtual ~IShaderLocation() { }	
			virtual void Enable()=0;
			virtual void Disable()=0;
			virtual void SetLocation(const ShaderLocalizator& localizator, ShaderCompilationReport& report)=0;
			// Public Attributes	
			std::string name;		// Location name
			int location;	// Location index
	};
	//-----------------------------------------------------------------------------
	template<class T>
	class GenericLocation : public IShaderLocation {
		public:
			GenericLocation (const std::string& name, const T* ressource);
			virtual ~GenericLocation ();
			virtual void Enable();
			virtual void Disable();
			virtual void SetLocation(const ShaderLocalizator& localizator, ShaderCompilationReport& report);
		private:
			// Forbid methods
			GenericLocation(const GenericLocation& copy);
			GenericLocation& operator=(const GenericLocation& copy);
			// Attributes
			const T* ressource;
	};
	//-----------------------------------------------------------------------------
	template<class T>
	class TextureLocation : public IShaderLocation {
		public:
			TextureLocation (const std::string& name, const Tools::SmartPointer<T>& texture);
			virtual ~TextureLocation();
			virtual void Enable();
			virtual void Disable();
			virtual void SetLocation(const ShaderLocalizator& localizator, ShaderCompilationReport& report);
		protected:
			// Forbid methods
			TextureLocation(const TextureLocation& copy);
			TextureLocation& operator=(const TextureLocation& copy);
			// Attributes
			int unit;
			Tools::SmartPointer<T> texture;
	};
	//-----------------------------------------------------------------------------
	typedef Tools::SmartPointer<IShaderLocation> TIShaderLocationPointer;



	//-----------------------------------------------------------------------------
	// Contexts
	//-----------------------------------------------------------------------------
	class IShaderContext {
		public:
			IShaderContext() { }	
			virtual ~IShaderContext() { }
			virtual void Enable()=0;
			virtual void Disable()=0;
			virtual void SetLocation(const ShaderLocalizator& localizator, ShaderCompilationReport& report)=0;
	};
	//-----------------------------------------------------------------------------
	class RessourceContext : public IShaderContext {
		public:
			RessourceContext();
			virtual ~RessourceContext();
			virtual void Enable();
			virtual void Disable();
			virtual void SetLocation(const ShaderLocalizator& localizator, ShaderCompilationReport& report);
			template<class T>
			void AddRessource(const std::string& name, const T& ressource);
			template<class T>
			void AddRessource(const std::string& name, const Tools::SmartPointer<T>& texture);			
		protected:
			// Forbid methods
			RessourceContext(const RessourceContext& copy);
			RessourceContext& operator=(const RessourceContext& copy);
			// Attributes
			typedef std::vector<TIShaderLocationPointer> TIShaderLocationPointerVector;
			TIShaderLocationPointerVector locations;
	};
	//-----------------------------------------------------------------------------
	class Mesh;
	class GeometryContext : public IShaderContext {
		public:
			GeometryContext(const Geometry::TMeshPointer& mesh, const Matrix::Matrix4& localToWorldMatrix);
			virtual ~GeometryContext();
			virtual void Enable();
			virtual void Disable();
			virtual void SetLocation(const ShaderLocalizator& localizator, ShaderCompilationReport& report);
		protected:
			// Type definition
			struct BufferLocation { VertexBufferType type; int location; };
			typedef std::vector<BufferLocation> TBufferLocationVector;
			// Forbid methods
			GeometryContext(const GeometryContext& copy);
			GeometryContext& operator=(const GeometryContext& copy);
			// Attributes
			Geometry::TMeshPointer mesh;
			const Matrix::Matrix4& localToWorldMatrix;
			TBufferLocationVector activeBuffers;
	};
	//-----------------------------------------------------------------------------
	typedef Tools::SmartPointer<IShaderContext> TIShaderContextPointer;



	//-----------------------------------------------------------------------------
	// Shader program
	//-----------------------------------------------------------------------------
	class ShaderProgram;
	typedef Tools::SmartPointer<ShaderProgram> TShaderProgramPointer;
	//-----------------------------------------------------------------------------	
	class ShaderProgram {
		public:				
			// Constructor			
			void GetLocations(ShaderLocalizator& localizator) const;
			void Enable() const;
			void Disable() const;
			static TShaderProgramPointer CreateShader(const std::string& vertexShader, const std::string& fragmentShader);
			TRenderingID GetRenderingID() const;
		private:
			ShaderProgram(const std::string& vertexShader, const std::string& fragmentShader);
			// Forbid copy or affectation
			ShaderProgram(const ShaderProgram& copy);
			ShaderProgram& operator=(const ShaderProgram& copy);
			// Attributs	
			TRenderingID programID;
	};
	
	
	
	//-----------------------------------------------------------------------------
	// Environnement
	//-----------------------------------------------------------------------------
	class ShaderEnvironnement;
	typedef Tools::SmartPointer<ShaderEnvironnement> TShaderEnvironnementPointer;
	//-----------------------------------------------------------------------------
	class ShaderEnvironnement {
		public:			
			void Add(ContextType type, const TIShaderContextPointer& context );
			void Compile(ShaderCompilationReport& report);
			TIShaderContextPointer& GetContext(ContextType type);
			TShaderProgramPointer& GetProgram();
			static TShaderEnvironnementPointer CreateEnvironnement( const TShaderProgramPointer& program );
		private:
			ShaderEnvironnement( const TShaderProgramPointer& program );
			// Forbid methods
			ShaderEnvironnement(const ShaderEnvironnement& copy);
			ShaderEnvironnement& operator=(const ShaderEnvironnement& copy);
			// Attributs
			TShaderProgramPointer program;
			TIShaderContextPointer contexts[3];
	};	

}

#include <Rendering/Shader.inl>

#endif //SHADER_HPP
